package com.elitesland.system.service.impl;

import com.elitesland.core.base.ApiCode;
import com.elitesland.core.exception.BusinessException;
import com.elitesland.system.convert.SysPermissionConvert;
import com.elitesland.system.entity.QSysPermissionDO;
import com.elitesland.system.entity.QSysRolePermissionDO;
import com.elitesland.system.entity.SysPermissionDO;
import com.elitesland.system.param.SysPermissionNewParam;
import com.elitesland.system.param.SysPermissionUpdateParam;
import com.elitesland.system.repo.SysPermissionRepo;
import com.elitesland.system.service.SysPermissionService;
import com.elitesland.system.vo.AntTreeNode;
import com.elitesland.system.vo.SysPermissionVO;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.val;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <pre>
 * [功能说明]
 * </pre>
 *
 * @author Mir
 * @date 2020/7/5
 */
@Service
public class SysPermissionServiceImpl implements SysPermissionService {

    private SysPermissionRepo sysPermissionRepo;

    private JPAQueryFactory jpaQueryFactory;

    @Autowired
    public void setSysPermissionRepo(SysPermissionRepo sysPermissionRepo) {
        this.sysPermissionRepo = sysPermissionRepo;
    }

    @Autowired
    public void setJpaQueryFactory(JPAQueryFactory jpaQueryFactory) {
        this.jpaQueryFactory = jpaQueryFactory;
    }

    private final QSysPermissionDO permissions = QSysPermissionDO.sysPermissionDO;

    private final QSysRolePermissionDO rolePermissions = QSysRolePermissionDO.sysRolePermissionDO;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Long createMenu(SysPermissionNewParam permission) throws BusinessException {
        val existOpt = sysPermissionRepo.findByCode(permission.getCode());
        if (existOpt.isPresent()) {
            throw new BusinessException("菜单：" + permission.getCode() + ", 已经存在，无法创建");
        }

        val tempPermission = SysPermissionConvert.INSTANCE.newParamToDO(permission);
        tempPermission.setPermType(0);
        if (permission.getParentId() == null || permission.getParentId() == 0) {
            tempPermission.setPath("/");
        } else {
            val parentOpt = getById(permission.getParentId());
            if (parentOpt.isEmpty()) {
                throw new BusinessException(ApiCode.PARAMETER_EXCEPTION, "父级菜单不存在");
            }
            val parent = parentOpt.get();
            tempPermission.setPath(parent.getPath() + parent.getId() + "/");
        }
        //todo 增加排序计算逻辑

//        tempPermission.setSortNo(0);
        val newPermission = sysPermissionRepo.save(tempPermission);
        return newPermission.getId();
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Long createAction(SysPermissionNewParam permission) throws BusinessException {
        val existOpt = sysPermissionRepo.findByCode(permission.getCode());
        if (existOpt.isPresent()) {
            throw new BusinessException("能力：" + permission.getCode() + ", 已经存在，无法创建");
        }

        if (permission.getParentId() == 0) {
            throw new BusinessException(ApiCode.PARAMETER_EXCEPTION, "能力必须挂在菜单之下");
        }
        val parentOpt = getById(permission.getParentId());
        if (parentOpt.isEmpty()) {
            throw new BusinessException(ApiCode.PARAMETER_EXCEPTION, "能力所属菜单不存在");
        }
        val parent = parentOpt.get();
        if (parent.getPermType() != 0) {
            throw new BusinessException(ApiCode.PARAMETER_EXCEPTION, "能力必须挂在菜单之下");
        }
        val tempPermission = SysPermissionConvert.INSTANCE.newParamToDO(permission);
        tempPermission.setPermType(1);
        //todo 增加排序计算逻辑

        tempPermission.setSortNo(0);
        tempPermission.setPath(parent.getPath() + parent.getId() + "/");

        val newPermission = sysPermissionRepo.save(tempPermission);
        return newPermission.getId();
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void update(SysPermissionUpdateParam perm) {
        val permOpt = sysPermissionRepo.findById(perm.getId());
        if (permOpt.isEmpty()) {
            throw new BusinessException("待更新的权限信息不存在");
        }
        val oldPerm = permOpt.get();
        if (StringUtils.isNotBlank(perm.getCode())) {
            oldPerm.setCode(perm.getCode());
        }
        if (StringUtils.isNotBlank(perm.getName())) {
            oldPerm.setName(perm.getName());
        }
        if (StringUtils.isNotBlank(perm.getPattern())) {
            oldPerm.setPattern(perm.getPattern());
        }
        if (StringUtils.isNotBlank(perm.getHttpMethod())) {
            oldPerm.setHttpMethod(perm.getHttpMethod());
        }
        oldPerm.setIsHidden(perm.getIsHidden());
        oldPerm.setSortNo(perm.getSortNo());

        val updatePerms = new ArrayList<>(Collections.singletonList(oldPerm));
        if (!perm.getParentId().equals(oldPerm.getParentId())) {
            sysPermissionRepo.findById(perm.getParentId()).ifPresent(pp -> {
                val oldSubPath = oldPerm.getPath() + oldPerm.getId() + "/";

                val perms = sysPermissionRepo.findByPathLike(oldPerm.getPath() + oldPerm.getId() + "%");

                oldPerm.setPath(pp.getPath() + pp.getId() + "/")
                        .setParentId(pp.getId());

                val subPath = oldPerm.getPath() + oldPerm.getId() + "/";
                perms.forEach(p -> {
                    p.setPath(p.getPath().replace(oldSubPath, subPath));
                });

                updatePerms.addAll(perms);
            });
        }
        sysPermissionRepo.saveAll(updatePerms);
    }

    @Override
    public List<AntTreeNode> listAllMenuTree() {
        val permission = QSysPermissionDO.sysPermissionDO;

        val menuItor = sysPermissionRepo.findAll(permission.permType.eq(0));
        val allMenus = Lists.newArrayList(menuItor);

        return buildMenuTree(allMenus, 0L);
    }

    @Override
    public List<AntTreeNode> listAllMenuWithActionTree() {
        val allPerms = sysPermissionRepo.findAll();

        return buildMenuTree(allPerms, 0L);
    }

    private List<AntTreeNode> buildMenuTree(List<SysPermissionDO> menus, Long pId) {
        val subMenus = menus.stream().filter(m -> m.getParentId().equals(pId))
                .sorted(Comparator.comparing(SysPermissionDO::getSortNo)).collect(Collectors.toList());

        val menuList = new ArrayList<AntTreeNode>();

        for (val subMenu : subMenus) {
            val antTreeNode = AntTreeNode.builder()
                    .key(subMenu.getId().toString())
                    .title(subMenu.getName())
                    .build();

            if (subMenu.getPermType() == 0) {
                val subNodes = buildMenuTree(menus, subMenu.getId());
                if (CollectionUtils.isNotEmpty(subNodes)) {
                    antTreeNode.setChildren(subNodes);
                }
            }

            menuList.add(antTreeNode);
        }
        return menuList;
    }

    @Override
    public List<AntTreeNode> listAllMenuWithActionTree(Long permId) {
        val permission = QSysPermissionDO.sysPermissionDO;

        Iterable<SysPermissionDO> menuItor;
        if (permId == 0) {
            menuItor = sysPermissionRepo.findAll();
        } else {
            val permOpt = sysPermissionRepo.findById(permId);
            if (permOpt.isEmpty()) {
                throw new BusinessException("对应权限节点不存在");
            }
            val perm = permOpt.get();
            if (perm.getPermType() == 1) {
                throw new BusinessException("操作功能没有下层能力");
            }

            menuItor = sysPermissionRepo.findAll(permission.path.like(perm.getPath() + perm.getId() + "/%"));
        }
        val allMenus = Lists.newArrayList(menuItor);

        return buildMenuTree(allMenus, permId);
    }

    @Override
    public Set<SysPermissionDO> listAllPermissionsByIds(List<Long> ids) {
        return Sets.newHashSet(sysPermissionRepo.findAllByIdIn(ids));
    }

    @Override
    public List<AntTreeNode> listPermTreeByIds(List<Long> ids) {
        return null;
    }

    @Override
    public Optional<SysPermissionVO> getById(Long id) {
        return sysPermissionRepo.findById(id).map(p -> {
            val vo = SysPermissionConvert.INSTANCE.doToVO(p);

            sysPermissionRepo.findById(p.getParentId()).ifPresent(par -> vo.setParentName(par.getName()));

            return vo;
        });
    }

    @Override
    public List<SysPermissionVO> listActionsByMenuId(Long id) {
        return sysPermissionRepo.findAllByParentIdAndPermType(id, 1)
                .stream().map(SysPermissionConvert.INSTANCE::doToVO).collect(Collectors.toList());
    }
}
